#pragma warning(disable:4996)

#include <windows.h>
#include <tchar.h>
#include <stdio.h>
#include <math.h>
#include "ImageEx.h"

#pragma comment(lib, "gdiplus.lib")

using	namespace	Gdiplus;

ULONG_PTR			CImageEx::m_GdiplusToken = 0;
CLSID				CImageEx::m_jpegClsid = { 0 };
CLSID				CImageEx::m_bmpClsid = { 0 };
CLSID				CImageEx::m_gifClsid = { 0 };
CLSID				CImageEx::m_pngClsid = { 0 };
CLSID				CImageEx::m_tiffClsid = { 0 };
EncoderParameters	CImageEx::m_EncoderParameters = { 0 };

CImageEx::CImageEx(void)
{
	m_pBitmap	= NULL;
	m_pBits		= NULL;
}

CImageEx::~CImageEx(void)
{
	if(m_pBitmap)
	{
		if(m_pBits)
		{
			m_pBitmap->UnlockBits(&m_bitmapData);
		}
		delete m_pBitmap;	
	}
}

int CImageEx::Initialize()
{
	GdiplusStartupInput	gdiplusStartupInput;	
	Status				ret;

	m_GdiplusToken	= NULL;
	ret				= GdiplusStartup(&m_GdiplusToken, &gdiplusStartupInput, NULL);
	if (ret != Ok)
	{
		m_GdiplusToken	= NULL;
		printf("Failed to initialize Gdiplus, status = %d", ret);
		return 0;
	}
	
	GetEncoderClsid(L"image/bmp", &m_bmpClsid);
	GetEncoderClsid(L"image/jpeg", &m_jpegClsid);
	GetEncoderClsid(L"image/gif", &m_gifClsid);
	GetEncoderClsid(L"image/tiff", &m_tiffClsid);
	GetEncoderClsid(L"image/png", &m_pngClsid);

	m_EncoderParameters.Count	= 1;
	m_EncoderParameters.Parameter[0].Guid	= EncoderQuality;
	m_EncoderParameters.Parameter[0].Type	= EncoderParameterValueTypeLong;
	m_EncoderParameters.Parameter[0].NumberOfValues	= 1;

	return 1;
}

void CImageEx::Release()
{
	if ( m_GdiplusToken )
	{
		GdiplusShutdown(m_GdiplusToken);
	}
}

int CImageEx::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT		num		= 0;		// number of image encoders
   UINT		size	= 0;		// size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo	= NULL;

   GetImageEncodersSize(&num, &size);

   if (size == 0)
   {
	   return	-1;  // Failure
   }

   pImageCodecInfo	= (ImageCodecInfo*) GlobalAlloc(GMEM_FIXED, size);
   if (pImageCodecInfo == NULL)
   {
	   return	-1;  // Failure
   }

   GetImageEncoders(num, size, pImageCodecInfo);

   for( UINT j = 0; j < num; ++j )
   {
	   if ( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
	   {
		   *pClsid	= pImageCodecInfo[j].Clsid;
		   GlobalFree(pImageCodecInfo);
		   return	1;								// Success, Modified by YH,Chen, the original one is "return -1;"
	   }
   }

   GlobalFree(pImageCodecInfo);
   return	-1;  // Failure
}

//load from file
int CImageEx::Load(LPCSTR filename)
{
	wstring	wstrFileName( UTF82WChar( (const BYTE *)filename, (int) strlen(filename) ) );
	return	Load(wstrFileName.c_str());
}

int CImageEx::Load(LPCWSTR filename)
{
	m_pBitmap	= Bitmap::FromFile(filename);

	if ( (m_pBitmap == NULL) || (m_pBitmap->GetLastStatus() != Ok) )
	{
		return 0;
	}
	
	return 1; //m_pBitmap->GetLastStatus() == Gdiplus::Ok;
}

//load from memory block
//copy to a new buffer
int CImageEx::Load(PBYTE pImageData, int size)
{
	PBYTE		pBuffer;
	IStream*	pStream	= NULL;

	pBuffer	= (PBYTE) GlobalAlloc(GMEM_FIXED, size);
	if (pBuffer == NULL)
	{
		return 0;
	}

	CopyMemory(pBuffer, pImageData, size);
	if ( CreateStreamOnHGlobal(pBuffer, true, &pStream) == S_OK )
    {
		m_pBitmap = Bitmap::FromStream(pStream);
		pStream->Release();								//release pBuffer automatically
    }
	else
	{
		return 0;
	}

	if ( (m_pBitmap == NULL) || (m_pBitmap->GetLastStatus() != Ok) )
	{
		return 0;
	}

	return 1;
}

int CImageEx::Save(LPCSTR filename)
{
	wstring	wstrFileName( UTF82WChar( (const BYTE *)filename, (int) strlen(filename) ) );
	return	Save(wstrFileName.c_str());
}

int CImageEx::Save(LPCWSTR filename)
{
	WCHAR		ext[256];
	ULONG		quality;
	
	_wsplitpath(filename, NULL, NULL, NULL, ext);	
	if ( (wcscmp(ext, L".jpg") == 0) || (wcscmp(ext, L"jpeg") == 0) )
	{
		quality	= 100;
		m_EncoderParameters.Parameter[0].Value	= &quality;
		m_pBitmap->Save(filename, &m_jpegClsid, &m_EncoderParameters);
	}
	else if ( wcscmp(ext, L".bmp") == 0 )
	{
		m_pBitmap->Save(filename, &m_bmpClsid, NULL);
	}
	else if ( wcscmp(ext, L".png") == 0 )
	{
		m_pBitmap->Save(filename, &m_pngClsid, NULL);
	}
	else if ( wcscmp(ext, L".gif") == 0 )
	{
		m_pBitmap->Save(filename, &m_gifClsid, NULL);
	}
	else if ( (wcscmp(ext, L".tif") == 0) || (wcscmp(ext, L".tiff") == 0) )
	{
		m_pBitmap->Save(filename, &m_tiffClsid, NULL);
	}
	
	if ( m_pBitmap->GetLastStatus() != Ok )
	{		
		return 0;
	}

	return 1;
}


CImageEx* CImageEx::Duplicate()
{
	CImageEx*	pDuplicate	= NULL;
	
	pDuplicate	= new CImageEx();

	pDuplicate->m_pBitmap	= m_pBitmap->Clone( 0, 0, GetWidth(), GetHeight(), m_pBitmap->GetPixelFormat() );

	if ( (pDuplicate->m_pBitmap == NULL) || (pDuplicate->m_pBitmap->GetLastStatus() != Ok) )
	{
		delete pDuplicate;
		return NULL;
	}

	return pDuplicate;
}

//we only processing 32bits image.
int CImageEx::ConvertToARGB32Bits()
{
	PixelFormat		pixel;
	Bitmap			*pTemp;
	ColorPalette	*pPalette;
	int				width, height, size;
	int				offset1, offset2, i, j;
	PBYTE			pBits;
	WORD			*pWords;
	RGBQUAD			*pRgb;
	Rect			rect;
	BitmapData		bitmapData;

	pixel	= m_pBitmap->GetPixelFormat();
	width	= m_pBitmap->GetWidth();
	height	= m_pBitmap->GetHeight();

	if ( (pixel != PixelFormat32bppARGB) && (pixel != PixelFormat32bppRGB) && (pixel != PixelFormat32bppPARGB) ) //true color
	{
		//create a temporary bitmap, 32 bits per pixel
		if ( (pTemp = new Bitmap(width, height, PixelFormat32bppARGB)) == NULL )
		{
			return 0;
		}

		rect.X		= 0;
		rect.Y		= 0;
		rect.Width	= width;
		rect.Height	= height;
		if ( (pTemp->LockBits(&rect, ImageLockModeRead | ImageLockModeWrite, PixelFormat32bppARGB, &bitmapData) != Ok) || (bitmapData.Scan0 == NULL) )
		{
			delete pTemp;
			return 0;
		}
		pRgb	= (RGBQUAD*) bitmapData.Scan0;

		//get source data
		if ( (m_pBitmap->LockBits(&rect, ImageLockModeRead, pixel, &m_bitmapData) != Ok) || (m_bitmapData.Scan0 == NULL) )
		{
			pTemp->UnlockBits(&bitmapData);
			delete pTemp;
			return 0;
		}
		pBits	= (PBYTE) m_bitmapData.Scan0;

		//processing
		switch(pixel)
		{
			case PixelFormat8bppIndexed:		     //256 color
				size	= m_pBitmap->GetPaletteSize();
				if ( (pPalette = (ColorPalette*) GlobalAlloc(GMEM_FIXED, size)) == NULL )
				{
					return 0;
				}
				m_pBitmap->GetPalette(pPalette, size);  	
				offset1	= 0;
				offset2	= 0;
				for ( i = 0; i < height; i++ )
				{
					for ( j = 0; j < width; j++ )
					{
						*((DWORD*) pRgb + offset1 + j)	= (pPalette->Entries[pBits[offset2 + j]]);	
						pRgb[offset1 + j].rgbReserved	= 255;
					}
					offset1	+= width;
					offset2	+= m_bitmapData.Stride;
				}
				GlobalFree(pPalette);
				break;
			case PixelFormat16bppRGB555:				//16 bits, 64K color
				//set 5 bits as the high 5 bits of every color channel.
				//0  RRRRR  GGGGG  BBBBB
				//15 14-10  9 - 5  4 - 0
				pWords	= (WORD*) pBits;
				offset1	= 0;
				offset2	= 0;
				for ( i = 0; i < height; i++ )
				{
					for ( j = 0; j < width; j++ )
					{
						pRgb[offset1 + j].rgbBlue		= (pWords[offset2 + j] & 0x001F) << 3;
						pRgb[offset1 + j].rgbGreen		= (pWords[offset2 + j] & 0x3E0) >> 2;
						pRgb[offset1 + j].rgbRed		= (pWords[offset2 + j] & 0x7C00) >> 7;
						pRgb[offset1 + j].rgbReserved	= 255;					
					}
					offset1	+= width;
					offset2	+= m_bitmapData.Stride / 2;
				}
				break;
			case PixelFormat24bppRGB:
				offset1	= 0;
				offset2	= 0;
				for ( i = 0; i < height; i++ )
				{
					for ( j = 0; j < width; j++ )
					{
						pRgb[offset1 + j].rgbBlue		= pBits[offset2+(3*j)+0];
						pRgb[offset1 + j].rgbGreen		= pBits[offset2+(3*j)+1];
						pRgb[offset1 + j].rgbRed		= pBits[offset2+(3*j)+2];	
						pRgb[offset1 + j].rgbReserved	= 255;
					}
					offset1	+= width;
					offset2	+= m_bitmapData.Stride;
				}
				break;
			default:
				break;
		}
		pTemp->UnlockBits(&bitmapData);
		m_pBitmap->UnlockBits(&m_bitmapData);

		delete m_pBitmap;
		m_pBitmap	= pTemp;
	}

	return 1;
}

int CImageEx::ConvertToRGB24Bits()
{
	PixelFormat		pixel;
	Bitmap			*pTemp;
	ColorPalette	*pPalette;
	Color			color;
	int				width, height, size;
	int				offset1, offset2, i, j;
	PBYTE			pBits;
	WORD			*pWords;
	BYTE			*pRgb;
	Rect			rect;
	BYTE			index;
	BitmapData		bitmapData;

	pixel	= m_pBitmap->GetPixelFormat();
	width	= m_pBitmap->GetWidth();
	height	= m_pBitmap->GetHeight();
	if ( pixel == PixelFormat24bppRGB )
	{
		return 1;
	}

	if ( (pTemp = new Bitmap(width, height, PixelFormat24bppRGB)) == NULL )
	{
		return 0;
	}
	rect.X		= 0;
	rect.Y		= 0;
	rect.Width	= width;
	rect.Height	= height;
	if ( (pTemp->LockBits(&rect, ImageLockModeRead | ImageLockModeWrite, PixelFormat24bppRGB, &bitmapData) != Ok) || (bitmapData.Scan0 == NULL) )
	{
		delete pTemp;
		return 0;
	}
	pRgb	= (PBYTE) bitmapData.Scan0;

	//get source data
	if ( (m_pBitmap->LockBits(&rect, ImageLockModeRead, pixel, &m_bitmapData) != Ok) || (m_bitmapData.Scan0 == NULL) )
	{
		pTemp->UnlockBits(&bitmapData);
		delete pTemp;
		return 0;
	}
	pBits	= (PBYTE) m_bitmapData.Scan0;

	//processing
	switch (pixel)
	{
		case PixelFormat4bppIndexed:
			size	= m_pBitmap->GetPaletteSize();
			if ( (pPalette = (ColorPalette*) GlobalAlloc(GMEM_FIXED, size)) == NULL )
			{
				return 0;
			}
			m_pBitmap->GetPalette(pPalette, size);  	
			offset1	= 0;
			offset2	= 0;
			for ( i = 0; i < height; i++ )
			{
				for ( j = 0; j < width; j++ )
				{
					index = pBits[offset2 + j/2];
					if ( j%2 == 0 )
					{
						index = index >> 4;
					}
					else
					{
						index = index & 0xF;
					}
					color = pPalette->Entries[index];
					pRgb[offset1+j*3+0]	= color.GetBlue();
					pRgb[offset1+j*3+1] = color.GetGreen();
					pRgb[offset1+j*3+2] = color.GetRed();
				}
				offset1 += bitmapData.Stride;
				offset2 += m_bitmapData.Stride;
			}
			GlobalFree(pPalette);
			break;
		case PixelFormat8bppIndexed:		     //256 color
			size	= m_pBitmap->GetPaletteSize();
			if ( (pPalette = (ColorPalette*) GlobalAlloc(GMEM_FIXED, size)) == NULL )
			{
				return 0;
			}
			m_pBitmap->GetPalette(pPalette, size);  	
			offset1	= 0;
			offset2	= 0;
			for ( i = 0; i < height; i++ )
			{
				for ( j = 0; j < width; j++ )
				{
					color = pPalette->Entries[pBits[offset2 + j]];
					pRgb[offset1+j*3+0] = color.GetBlue();
					pRgb[offset1+j*3+1] = color.GetGreen();
					pRgb[offset1+j*3+2] = color.GetRed();
				}
				offset1 += bitmapData.Stride;
				offset2 += m_bitmapData.Stride;
			}
			GlobalFree(pPalette);
			break;
		case PixelFormat16bppRGB555:			//16 bits, 64K color
			//set 5 bits as the high 5 bits of every color channel.
			//0  RRRRR  GGGGG  BBBBB
			//15 14-10  9 - 5  4 - 0
			pWords	= (WORD*) pBits;
			offset1	= 0;
			offset2	= 0;
			for ( i = 0; i < height; i++ )
			{
				for ( j = 0; j < width; j++ )
				{
					pRgb[offset1 + j*3 + 2]	= (pWords[offset2 + j] & 0x001F) << 3;
					pRgb[offset1 + j*3 + 1] = (pWords[offset2 + j] & 0x3E0) >> 2;
					pRgb[offset1 + j*3 + 0] = (pWords[offset2 + j] & 0x7C00) >> 7;
				}
				offset1 += bitmapData.Stride;
				offset2 += m_bitmapData.Stride / 2;
			}
			break;
		case PixelFormat32bppRGB:
		case PixelFormat32bppPARGB:
			offset1	= 0;
			offset2	= 0;
			for ( i = 0; i < height; i++ )
			{
				for ( j = 0; j < width; j++ )
				{
					pRgb[offset1 + j*3 + 2] = pBits[offset2 + j*4 + 2];
					pRgb[offset1 + j*3 + 1] = pBits[offset2 + j*4 + 1];
					pRgb[offset1 + j*3 + 0] = pBits[offset2 + j*4 + 0];
				}
				offset1 += bitmapData.Stride;
				offset2 += m_bitmapData.Stride;
			}
			break;
		case PixelFormat32bppARGB:
			offset1	= 0;
			offset2	= 0;
			for(i = 0; i < height; i++)
			{
				for(j = 0; j < width; j++)
				{
					pRgb[offset1 + j*3 + 2] = pBits[offset2 + j*4 + 2] * pBits[offset2 + j*4 + 3] / 255;
					pRgb[offset1 + j*3 + 1] = pBits[offset2 + j*4 + 1] * pBits[offset2 + j*4 + 3] / 255;
					pRgb[offset1 + j*3 + 0] = pBits[offset2 + j*4 + 0] * pBits[offset2 + j*4 + 3] / 255;
				}
				offset1 += bitmapData.Stride;
				offset2 += m_bitmapData.Stride;
			}
			break;
		default:
			break;
	}
	
	pTemp->UnlockBits(&bitmapData);	
	m_pBitmap->UnlockBits(&m_bitmapData);

	delete m_pBitmap;
	m_pBitmap	= pTemp;

	return 1;
}

int CImageEx::ConvertToThumbnail()
{
	// resize the image to 160 * 160
	int		iWidth, iHeight;
	int		width, height;
	int		ret;

	if ( m_pBitmap == NULL )
	{
		return	0;
	}

	width	= 160;
	height	= 160;
	iWidth	= GetWidth();
	iHeight	= GetHeight();

	if ( iWidth > iHeight )
	{
		if ( iWidth <= width )
		{
			return	1;
		}
		height	= width * iHeight / iWidth;
	}
	else
	{
		if ( iHeight <= height )
		{
			return	1;
		}
		width	= height * iWidth / iHeight;
	}

	ret	= Resize(width, height);

	return	ret;
}

void CImageEx::Unload()
{	
	delete m_pBitmap;
	m_pBitmap	= NULL;
}

int CImageEx::Draw(HWND hWnd, const RECT& srcRect, const RECT& destRect)
{
	HDC		hDC;
	int		ret;

	hDC	= GetDC(hWnd);
	if ( hDC == NULL )
	{
		return 0;
	}
	ret	= Draw(hDC, srcRect, destRect);
	ReleaseDC(hWnd, hDC);

	return ret;
}

int CImageEx::Draw(HDC hDC, const RECT& srcRect, const RECT& destRect)
{
	Graphics	graphics(hDC);
	RectF		dest(	(REAL) destRect.left,
						(REAL) destRect.top, 
						(REAL) (destRect.right - destRect.left), 
						(REAL) (destRect.bottom - destRect.top) );	

	if ( graphics.DrawImage(	m_pBitmap, dest, (REAL) srcRect.left, (REAL) srcRect.top,
								(REAL) (srcRect.right - srcRect.left), (REAL) (srcRect.bottom - srcRect.top), 
								UnitPixel, NULL, NULL, NULL ) != Ok )
		return 0;

	return 1;
}

int CImageEx::Resize(IN const int Width,IN const int Height)
{
	Image	*pNew	= m_pBitmap->GetThumbnailImage(Width,Height);
	WCHAR	szFileName[MAX_PATH]	= L"Temp.bmp";
	pNew->Save(szFileName, &m_bmpClsid, NULL);
	delete	pNew;

	Bitmap	*pNewBMP	= Bitmap::FromFile(szFileName);
	if ( NULL == pNewBMP )
	{
		return 0;
	}

	delete m_pBitmap;
	m_pBitmap = pNewBMP;
	return 1;
}

PBYTE CImageEx::GetBits(RECT* pRect, int format)
{	
	Rect		rect;
	PixelFormat	pixel;
	
	if(pRect == NULL)
	{
		rect.X		= 0;
		rect.Y		= 0;
		rect.Width	= m_pBitmap->GetWidth();
		rect.Height	= m_pBitmap->GetHeight();
	}
	else
	{
		rect.X		= pRect->left;
		rect.Y		= pRect->top;
		rect.Width	= pRect->right - pRect->left;
		rect.Height	= pRect->bottom - pRect->top;
	}

	switch(format)
	{
		case 8:
			pixel = PixelFormat8bppIndexed;
			break;
		case 16:
			pixel = PixelFormat16bppRGB555;
			break;
		case 24: //24 bits/pixel
			pixel = PixelFormat24bppRGB;
			break;
		case 32:
			pixel = PixelFormat32bppARGB;
			break;	
		default:
			return NULL;
	}

	if ( m_pBitmap->LockBits(&rect, ImageLockModeRead | ImageLockModeWrite, pixel, &m_bitmapData) != Ok )
	{
		return NULL;
	}
	
	return (PBYTE) m_bitmapData.Scan0;
}

int CImageEx::FreeBits()
{
	if ( m_pBitmap->UnlockBits(&m_bitmapData) != Ok )
	{
		printf("failed to unlock bits\n");
		return	0;
	}

	return	1;
}

//convert to 256 level gray scale
void CImageEx::GrayScale()
{
	RGBQUAD	*pBits, *pRgb;
	int		i, j, width, height;
	BYTE	grayscale;	

	pBits	= (RGBQUAD*) GetBits();
	if ( pBits == NULL )
	{
		return;
	}

	width	= GetWidth();
	height	= GetHeight();
	for ( i = 0; i < height; i++ )
	{		
		for ( j = 0; j < width; j++ )
		{
			pRgb			= pBits++;
			grayscale		= (pRgb->rgbRed + pRgb->rgbGreen + pRgb->rgbBlue) / 3;
			pRgb->rgbRed	= grayscale;
			pRgb->rgbGreen	= grayscale;
			pRgb->rgbBlue	= grayscale;
		}
	}

	FreeBits();
}

void CImageEx::Negative()
{
	RGBQUAD	*pBits, *pRgb;
	int		i, j, width, height;
	
	pBits = (RGBQUAD*) GetBits();
	if ( pBits == NULL )
	{
		return;
	}

	width	= GetWidth();
	height	= GetHeight();
	for ( i = 0; i < height; i++ )
	{		
		for ( j = 0; j < width; j++ )
		{
			pRgb			= pBits++;
			pRgb->rgbRed	= ~pRgb->rgbRed;
			pRgb->rgbGreen	= ~pRgb->rgbGreen;
			pRgb->rgbBlue	= ~pRgb->rgbBlue;
		}
	}

	FreeBits();
}

int CImageEx::RgbToGray(BYTE red, BYTE green, BYTE blue)
{
	return (red + green + blue) / 3;
}

void CImageEx::HistogramEqualize()
{
	RGBQUAD	*pBits;
	int		i, j, temp, width, height, totalPixels;
	int     count[256], map[256];

	pBits	= (RGBQUAD*) GetBits();
	if ( pBits == NULL )
	{
		return;
	}

	width	= GetWidth();
	height	= GetHeight();
	for ( i = 0; i < height; i++ )
	{
		for ( j = 0; j < width; j++ )
		{
			count[RgbToGray(pBits->rgbRed, pBits->rgbGreen, pBits->rgbBlue)]++;
			pBits++;
		}
	}
	
	//calc map
	temp		= 0;
	totalPixels	= width * height;
	for ( i = 0; i < 256; i++ )
	{
		temp	+= count[i];
		map[i]	= (temp * 255) / totalPixels;
	}
	
	pBits	-= totalPixels;
	for ( i = 0; i < height; i++ )
	{
		for ( j = 0; j < width; j++ )
		{
			pBits->rgbRed	= map[pBits->rgbRed];
			pBits->rgbGreen	= map[pBits->rgbGreen];
			pBits->rgbBlue	= map[pBits->rgbBlue];
			pBits++;
		}
	}
	FreeBits();
}

float CImageEx::EvalColorful()
{
	int			nWidth, nHeight, i, j;
	float		rg, yb, M3, Urg, Uyb, Org = 0.0f, Oyb = 0.0f, t1, t2;
	float		*pfImage[2];	//one for rg, the other for yb
	const float level[] = { 0, 15, 33, 45, 59, 109 };
	RGBQUAD		*pRgb = (RGBQUAD*) GetBits();
	
	nWidth		= GetWidth();
	nHeight		= GetHeight();
	pfImage[0]	= (float*) GlobalAlloc(GMEM_FIXED | GMEM_ZEROINIT, nWidth * nHeight * sizeof(float) * 2);
	pfImage[1]	= pfImage[0] + nWidth * nHeight;
	
	t1	= 0.0f;
	t2	= 0.0f;
	for ( i = 0; i < nHeight; i++ )
	{
		for ( j = 0; j < nWidth; j++ )
		{
			rg	= (float) ( pRgb->rgbRed - pRgb->rgbGreen );
			yb	= ( pRgb->rgbRed + pRgb->rgbGreen ) / 2.0f - pRgb->rgbBlue;
			pfImage[0][i * nWidth + j]	= rg;
			pfImage[1][i * nWidth + j]	= yb;
			pRgb++;
			t1	+= rg;
			t2	+= yb;
		}
	}
	FreeBits();
	
	Urg	= t1 / (nWidth * nHeight);
	Uyb	= t2 / (nWidth * nHeight);
	for ( i = 0; i < nHeight; i++ )
	{
		for ( j = 0; j < nWidth; j++ )
		{
			Org	+= (pfImage[0][i * nWidth + j] - Urg) * (pfImage[0][i * nWidth + j] - Urg);
			Oyb	+= (pfImage[1][i * nWidth + j] - Uyb) * (pfImage[1][i * nWidth + j] - Uyb);
		}
	}
	GlobalFree(pfImage[0]);

	Org	= (float) sqrt(Org / (nWidth * nHeight));
	Oyb	= (float) sqrt(Oyb / (nWidth * nHeight));
	
	M3	= Org + 0.3f * Oyb;
	for ( i = 0; i < sizeof(level) / sizeof(float); i++ )
	{
		if ( M3 < level[i] )
		{
			break;
		}
	}

	i	= (i - 1 > 0) ? i - 1 : 0;

	return	(float) i / (sizeof(level) / sizeof(float));
}

#define	PIXEL_MAX_VALUE		255
#define	CLIP(x)				min(PIXEL_MAX_VALUE, max(0, (x)) )

float	CImageEx::EvalContrast()
{
	RGBQUAD		*pRgb;
	int			nWidth, nHeight;
	float		bins[256] = { 0 };
	int			up = 230, down = 26;

	nWidth	= GetWidth();
	nHeight	= GetHeight();
	pRgb	= (RGBQUAD*) GetBits();

    double		dMean = 0, dStdVar = 0;
    LONGLONG	llSum = 0, llSumSquare = 0;
    int			iVal,iSize = nHeight * nWidth;
    RGBQUAD		*pCurr = pRgb;
	BYTE		bIntensity;
    int			iPos = 0;

	for ( llSum = 0, iPos = 0; iPos < iSize; iPos++, pCurr++ )
    {
		bIntensity	= (BYTE )(CLIP((int)(( 0.257 * pCurr->rgbRed + 0.504 * pCurr->rgbGreen + 0.098 * pCurr->rgbBlue + 0.5) + 16)));
        iVal		= (int) (bIntensity);
        llSum		+= iVal; 
        llSumSquare	+= iVal * iVal;
    }
	FreeBits();

    dMean	= (double ) llSum / (double) (iSize);
    dStdVar	= sqrt( ((double)llSumSquare - dMean * llSum) / (double) (iSize - 1) );
    dStdVar /= (double)PIXEL_MAX_VALUE;

	return (float)dStdVar;                                                                                                                                                                                
}

double CImageEx::EvalSize(void)
{
	const int	iBinSize	= 256;
	const int	iLowBound	= 16;
	const int	iUpperBound	= 2515;
	int			iWidth, iHeight, iImageSize, iBinnedImageSize;
	double		dSizeRatio;

	iWidth				= GetWidth();
	iHeight				= GetHeight();
	iImageSize			= iWidth * iHeight;
	iBinnedImageSize	= ( iImageSize + iBinSize - 1 ) / iBinSize;

	if ( iBinnedImageSize < iLowBound )
	{
		return 0.0;
	}
	else if ( iBinnedImageSize >= iUpperBound )
	{
		return 1.0;
	}
	else
	{
		//double rgdCoeff[] = // x ^ 6
		//{
		//	0.106509580562
		//	8.65697138712,
		//	-46.6221421585,
		//	132.973019217,
		//	-199.48832827,
		//	149.440988079,
		//	-44.0874554422,
		//};
		double rgdCoeff[]	= {								// X ^ 8
								0.0368964134041,
								13.0335655654,
								-114.608287053,
								580.403017382,
								-1718.23219909,
								3029.03755049,
								-3124.63984737,
								1738.17488387,
								-402.219754816
							  };
		int		iOrder		= sizeof(rgdCoeff) / sizeof(double);
		double	dVar		= 1.0;
		double	dImageSize	= (double)(iBinnedImageSize-iLowBound) / (double)(iUpperBound - iLowBound);

		dSizeRatio	= 0.0;
        for ( int i = 0; i < iOrder ; i++ )
		{
			dSizeRatio	+= dVar * rgdCoeff[i];
			dVar		*= dImageSize;
		}

		if ( (dSizeRatio > 1.0) || (dImageSize > 0.95) )
		{
			dSizeRatio = 1.0;
		}

	}

	return dSizeRatio;
}

